
// Includes

#include <GL/glut.h>
#include <fstream>
#include <iostream>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ample   800
#define alt     600

// DEFINICIONS

typedef struct{
	GLfloat maxim[31]; //Llista de temperatures maximes
	GLfloat minim[31]; //Llista de temperatures minimes
	GLfloat mitja[31]; //Llista de temperatures mitjes
	int diaMes[31];    //Dia del mes que correspon a la posicio
	int ndies;
}mes;

enum { BEZIER, BSPLINE, POLIGONAL };

//GLOBALS

int posx1=30;
int posy1=170;
int posx2=425;
int posy2=170;
int ampleX=350;
int alturaY=350;
int maxDiesMes=31;
int stepX=ampleX/maxDiesMes;
int i;
int tempMax=20;

int mostrarMin=-1;
int mostrarMax=-1;
int mostrarMit=1;
mes dades;

static float bspline[4][4] = {
  {1.0/6.0, 4.0/6.0, 1.0/6.0, 0.0},
  {0.0, 4.0/6.0, 2.0/6.0, 0.0},
  {0.0, 2.0/6.0, 4.0/6.0, 0.0},
  {0.0, 1.0/6.0, 4.0/6.0, 1.0/6.0}
};

static float id[4][4] = {
  {1.0, 0.0, 0.0, 0.0},
  {0.0, 1.0, 0.0, 0.0},
  {0.0, 0.0, 1.0, 0.0},
  {0.0, 0.0, 0.0, 1.0}
};

int tipusCorba=BEZIER;

//METODES

void MATMULT(float m[4][4],float v[4][3],float r[4][3])
{
  int i,j,k;

  for(i=0;i<4;i++)//MATMULT
    for(j=0;j<3;j++)
      for(k=0, r[i][j]=0.0 ;k<4;k++)
			 r[i][j]+= m[i][k] * v[k][j];

}

void escriureText(float x, float y, const char *string, void *font)
{
   int len, i;
   glRasterPos3f(x, y, 0);
   len = (int) strlen(string);
   for (i = 0; i < len; i++) {
       glutBitmapCharacter(font, string[i]);
    }
}





void dibuixarEixos(int posx_inicial, int posy_inicial, int grafica)
{
	glBegin(GL_LINES);
		glVertex2i(posx_inicial,posy_inicial+alturaY);
		glVertex2i(posx_inicial,posy_inicial);
	glEnd();

    glBegin(GL_LINES);
		glVertex2i(posx_inicial,posy_inicial+alturaY/2);
		glVertex2i(posx_inicial+ampleX,posy_inicial+alturaY/2);
	glEnd();

	/*Escrivim nom grafica*/
	if(grafica == 1)
    escriureText(posx_inicial+ampleX/2-55, posy_inicial+alturaY+10,"Metode X dies",GLUT_BITMAP_HELVETICA_18);
    else escriureText(posx_inicial+ampleX/2-75, posy_inicial+alturaY+10,"Metode max min locals",GLUT_BITMAP_HELVETICA_18);
}

void liniesEixX(int posx_inicial, int posy_inicial)
{
    char buffer [33];
    bool senar = true;
    int desfas;
    for (i=1; i<maxDiesMes+1; i++){
    	glBegin(GL_LINES);
		glVertex2i(((ampleX)/maxDiesMes)*i+posx_inicial,posy_inicial+alturaY/2+5);
		glVertex2i(((ampleX)/maxDiesMes)*i+posx_inicial,posy_inicial+alturaY/2-5);
        glEnd();
        /*Escrivim els valors de la x*/
        if (senar == true)
        {
            sprintf (buffer, "%i", i);
            if (i>9) desfas=6; else desfas=3;
            escriureText(((ampleX)/maxDiesMes)*i+posx_inicial-desfas,posy_inicial+alturaY/2-15,buffer,GLUT_BITMAP_HELVETICA_10);
            senar = false;
        }else senar = true;
    }
    /*Escrivim llegenda*/
    escriureText(posx_inicial+ampleX-7, posy_inicial+(alturaY/2)+6,"Dies",GLUT_BITMAP_HELVETICA_12);
}

void liniesEixY(int posx_inicial, int posy_inicial)
{
    char buffer [33];

    for (i=1; i<tempMax; i+=tempMax/5){
        /*Dibuixem les l�nies superiors*/
        glBegin(GL_LINES);
		glVertex2i(posx_inicial-5,posy_inicial+alturaY/2+i*alturaY/20);
		glVertex2i(posx_inicial+5,posy_inicial+alturaY/2+i*alturaY/20);
        glEnd();
        /*Dibuixem les l�nies inferiors*/
        glBegin(GL_LINES);
		glVertex2i(posx_inicial-5,posy_inicial+alturaY/2-i*alturaY/20);
		glVertex2i(posx_inicial+5,posy_inicial+alturaY/2-i*alturaY/20);
        glEnd();
        /*Escrivim els valors de la y per cada 10*/
        sprintf (buffer, "%i", i*(-1));
        escriureText(posx_inicial-24,posy_inicial+alturaY/2-i*alturaY/20-3,buffer,GLUT_BITMAP_HELVETICA_10);
        sprintf (buffer, "%i", i*1);
        escriureText(posx_inicial-18,posy_inicial+alturaY/2+i*alturaY/20-3,buffer,GLUT_BITMAP_HELVETICA_10);
    }
    /*Escrivim llegenda*/
    escriureText(posx_inicial-18, posy_inicial+alturaY+8,"Graus C",GLUT_BITMAP_HELVETICA_12);
}

void pintaPoligonal(float *dades, int *diesMes, int nDades)
{
	int i;
	int stepY=alturaY/(tempMax*2);

	glBegin(GL_LINE_STRIP);
	for(i=0;i<nDades;i++)
	{
		glVertex2f(diesMes[i]*stepX,dades[i]*stepY);
	}
	glEnd();
}

void pintaBSPLine(GLfloat *dades, int *diesMes, int nDades)
{
	int stepY=alturaY/(tempMax*2);
	GLfloat pControl[maxDiesMes][3], pTrasladats[4][3];

	for(i=0;i<nDades;i++)
	{
		pControl[i][0]=diesMes[i]*stepX;
		pControl[i][1]=dades[i]*stepY;
	}
	float trasladades[maxDiesMes];
	int i=0;
	while(i+3 < nDades)
	{
		MATMULT(bspline,&pControl[i],pTrasladats);
		glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 4, &pTrasladats[0][0]);
		glMapGrid1f(30, 0.0, 1.0);
		glEnable(GL_MAP1_VERTEX_3);
		glEvalMesh1(GL_LINE, 0, 30);
		i++;
	}
}

void pintaBezier(float *dades, int *diesMes, int nDades)
{
	//Pinta punts de control
	int stepY=alturaY/(tempMax*2);
	int ad;
	GLfloat px,py;
	double a,b;
	int i;
	GLfloat pControl[maxDiesMes][3], pTrasladats[4][3];
	for(i=0;i<nDades;i++)
	{
		pControl[i][0]=diesMes[i]*stepX;
		pControl[i][1]=dades[i]*stepY;
	}
	i=0;
	while (i+3 < nDades)
	{
		MATMULT(id,&pControl[i],pTrasladats);
		a=1.0;
		b=1.0-a;
		glBegin(GL_LINE_STRIP);
		for (int j = 0; j <= 100; j++)
		{
			 px = pControl[i][0]*a*a*a + pControl[i+1][0]*3*a*a*b + pControl[i+2][0]*3*a*b*b + pControl[i+3][0]*b*b*b;
			 py= pControl[i][1]*a*a*a + pControl[i+1][1]*3*a*a*b + pControl[i+2][1]*3*a*b*b + pControl[i+3][1]*b*b*b;
			 glVertex2f(px, py);
			 a -= (1.0/100);//0.05;
			 b = 1.0 - a;
		}
		glEnd();
		/* Advance to the next segment */
		i += 3;
	}
}

void pinta(float *dades, int *diesMes, int nDades)
{
    //Pinta punts de control
	int stepY=alturaY/(tempMax*2);
	glPointSize(4.0);
	glBegin(GL_POINTS);
	for(int i=0;i<nDades;i++)
		glVertex2f(diesMes[i]*stepX,dades[i]*stepY);
	glEnd();
	glPointSize(1.0);

	//Pinta la corba
	switch(tipusCorba)
	{
		case POLIGONAL: pintaPoligonal(dades,diesMes, nDades);break;
		case BSPLINE: pintaBSPLine(dades, diesMes, nDades);break;
		case BEZIER: pintaBezier(dades, diesMes, nDades);break;
	}
}

int reduirDadesXDies(float *dades, int *diaMes, float *reduides, int *diaMesReduides, int nDades)
{
	int i,j;
	j=0;
	for(i=0;i<nDades;i++)
	{
		if (i%2==0){
			reduides[j]=dades[i];
			diaMesReduides[j++]=diaMes[i];
		}
	}

	return j;
}

int signe(float x1, float y1, float x2, float y2)
{
	float pendent;
	float v1, v2;
	v1=x2-x1;
	v2=y2-y1;
	pendent=v2/v1;
	//printf("%f,%f %f,%f pendent %f\n",x1,y1,x2,y2,pendent);
	if (pendent>0) return 1;
	else return -1;
}

int reduirMaxMin(float *dades, int *diaMes, float *reduides, int *diaMesReduides, int nDades)
{
	int i,j,k=1;
	int s;
	j=0;
	s=signe(diaMes[0], dades[0],diaMes[1], dades[1]);
	reduides[j]=dades[0];
	diaMesReduides[j++]=diaMes[0];

	for(i=1;i<nDades-1;i++)
	{
		if ((s!=signe(diaMes[i], dades[i],diaMes[i+1], dades[i+1]))||(k>2))
		{
			reduides[j]=dades[i];
			diaMesReduides[j++]=diaMes[i];
			k=1;
		}else k++;
		s=signe(diaMes[i], dades[i],diaMes[i+1], dades[i+1]);
	}
	return j;
}

void pintarEixos()
{
    /*Dibuixem els primers eixos.*/
    dibuixarEixos(posx1, posy1, 1);
    /*Dibuixem les l�nies de separaci� dels d�es a l'eix de les X de la primera grafica.*/
    liniesEixX(posx1, posy1);
    /*Dibuixem les l�nies de separaci� de la temperatura a l'eix de les Y de la primera grafica.*/
    liniesEixY(posx1, posy1);
    /*Dibuixem el segon eix de simetria*/
    dibuixarEixos(posx2, posy2, 2);
    /*Dibuixem les l�nies de separaci� dels dies de la primera grafica.*/
    liniesEixX(posx2, posy2);
    /*Dibuixem les l�nies de separaci� de la temperatura a l'eix de les Y de la segona grafica.*/
    liniesEixY(posx2, posy2);
}
void escriureTitol()
{
    glColor3f(0.0,0.0,0.0);
    escriureText(ample/2-ampleX/4,posy1+alturaY+50,"GESTOR DE TEMPERATURES",GLUT_BITMAP_HELVETICA_18);
}

void escriureAutors()
{
    glColor3f(0.0,0.0,0.0);
    escriureText(ample-ampleX/2,posy1-100,"Autors:",GLUT_BITMAP_HELVETICA_18);
    escriureText(ample-ampleX/2,posy1-120,"Roger Pes Buenestado",GLUT_BITMAP_HELVETICA_12);
    escriureText(ample-ampleX/2,posy1-135,"Joan Sole Gavalda",GLUT_BITMAP_HELVETICA_12);
}

void llegenda()
{
    escriureText(posx1-24,posy1-40,"Llegenda:",GLUT_BITMAP_HELVETICA_18);
    escriureText(posx1,posy1-60,"Temperatures maximes originals.",GLUT_BITMAP_HELVETICA_12);
    escriureText(posx1,posy1-75,"Temperatures maximes reduides.",GLUT_BITMAP_HELVETICA_12);
    escriureText(posx1,posy1-90,"Temperatures mitjanes originals.",GLUT_BITMAP_HELVETICA_12);
    escriureText(posx1,posy1-105,"Temperatures mitjanes reduides.",GLUT_BITMAP_HELVETICA_12);
    escriureText(posx1,posy1-120,"Temperatures minimes originals.",GLUT_BITMAP_HELVETICA_12);
    escriureText(posx1,posy1-135,"Temperatures minimes reduides.",GLUT_BITMAP_HELVETICA_12);

    glColor3f(1.0,0.0,0.0);
    glBegin(GL_LINES);
		glVertex2i(posx1-24,posy1-57);
		glVertex2i(posx1-4,posy1-57);
	glEnd();
	glColor3f(0.9,0.7,0.0);
	glBegin(GL_LINES);
		glVertex2i(posx1-24,posy1-72);
		glVertex2i(posx1-4,posy1-72);
	glEnd();

    glColor3f(0.0,1.0,0.0);
    glBegin(GL_LINES);
		glVertex2i(posx1-24,posy1-87);
		glVertex2i(posx1-4,posy1-87);
	glEnd();
    glColor3f(0.7,0.0,0.9);
    glBegin(GL_LINES);
		glVertex2i(posx1-24,posy1-102);
		glVertex2i(posx1-4,posy1-102);
	glEnd();

    glColor3f(0.0,0.0,1.0);
	glBegin(GL_LINES);
		glVertex2i(posx1-24,posy1-117);
		glVertex2i(posx1-4,posy1-117);
	glEnd();
	glColor3f(0.0,0.7,0.9);
	glBegin(GL_LINES);
		glVertex2i(posx1-24,posy1-132);
		glVertex2i(posx1-4,posy1-132);
	glEnd();
}

void Pintar( void )
{
    int stepX=ampleX/dades.ndies;
    int nReduides;
    float reduides[31];
    int diaMesReduit[31];

	/*1r netejem el buffer de pantalla*/
	glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(0.0, 0.0, 0.0);

    pintarEixos();
	/*Pintem llegenda*/
	llegenda();
    /*Escrivim el nom dels autors.*/
	escriureAutors();
	/*Escrivim titol*/
	escriureTitol();

    /*Pintem les gr�fiques*/
	glColor3f(0.3, 0.7, 0.3);
	//Ens trasladem a la primera grafica
	glTranslatef(posx1,posy1+alturaY/2,0.0);
	if(mostrarMin>0)
	{
		glColor3f(0.0,0.0,1.0);
		pinta(dades.minim,dades.diaMes,dades.ndies);
		nReduides=reduirDadesXDies(dades.minim,dades.diaMes,reduides, diaMesReduit,dades.ndies);
		glColor3f(0.0,0.7,0.9);
		pinta(reduides,diaMesReduit,nReduides);
	}

	if(mostrarMax>0)
	{
		glColor3f(1.0,0.0,0.0);
		pinta(dades.maxim,dades.diaMes,dades.ndies);
		nReduides=reduirDadesXDies(dades.maxim,dades.diaMes,reduides, diaMesReduit,dades.ndies);
		glColor3f(0.9,0.7,0.0);
		pinta(reduides,diaMesReduit,nReduides);
	}
	if(mostrarMit>0)
	{
		glColor3f(0.0,1.0,0.0);
		pinta(dades.mitja,dades.diaMes,dades.ndies);
		nReduides=reduirDadesXDies(dades.mitja,dades.diaMes,reduides, diaMesReduit,dades.ndies);
		glColor3f(0.7,0.0,0.9);
		pinta(reduides,diaMesReduit,nReduides);
	}
	//Restem perqu� torni a repintar del principi.
    glTranslatef(-posx1,-posy1-alturaY/2,0.0);

    //Ens trasladem a la segona grafica
	glTranslatef(posx2,posy2+alturaY/2,0.0);
	if(mostrarMin>0)
	{
		glColor3f(0.0,0.0,1.0);
		pinta(dades.minim,dades.diaMes,dades.ndies);
		nReduides=reduirMaxMin(dades.minim,dades.diaMes,reduides, diaMesReduit,dades.ndies);
		glColor3f(0.0,0.7,0.9);
		pinta(reduides,diaMesReduit,nReduides);
	}

	if(mostrarMax>0)
	{
		glColor3f(1.0,0.0,0.0);
		pinta(dades.maxim,dades.diaMes,dades.ndies);
		nReduides=reduirMaxMin(dades.maxim,dades.diaMes,reduides, diaMesReduit,dades.ndies);
		glColor3f(0.9,0.7,0.0);
		pinta(reduides,diaMesReduit,nReduides);
	}
	if(mostrarMit>0)
	{
		glColor3f(0.0,1.0,0.0);
		pinta(dades.mitja,dades.diaMes,dades.ndies);
		nReduides=reduirMaxMin(dades.mitja,dades.diaMes,reduides, diaMesReduit,dades.ndies);
		glColor3f(0.7,0.0,0.9);
		pinta(reduides,diaMesReduit,nReduides);
	}

	//Restem perqu� torni a repintar del principi.
	glTranslatef(-posx2,-posy2-alturaY/2,0.0);

	/*I finalment ho enviem tot a la pantalla*/
	glutSwapBuffers();
}

float max(float a, float b)
{
    return (a > b ? a : b);
}


void llegirDades()
{
	int dia,i;
	char linea[100];
	float mitja,maxim,minim;
    dades.ndies = 0;tempMax=10;
    FILE *arxiuDades;
    arxiuDades = fopen("./dades.txt", "r");
    if (arxiuDades!=NULL)
    {
    	i=0;
		while(!feof(arxiuDades))
		{
			fgets(linea,100,arxiuDades);
			if(linea[0]!='/')
			{
				sscanf(linea,"%d%f%f%f",&dia,&mitja,&maxim,&minim);
				dades.maxim[i]=maxim;
				dades.minim[i]=minim;
				dades.mitja[i]=mitja;
				dades.diaMes[i++]=dia;
				tempMax=max(tempMax,abs(maxim));
				tempMax=max(tempMax,abs(minim));
			}
		}
		dades.ndies=i;
		fclose(arxiuDades);
		printf("Nombre de dades llegides: %d\n",dades.ndies);
    }else{
    	printf("No trobo l'arxiu \n");
    }
}

void menuPrincipal(int id){

	  switch (id) {
		case 4 :exit(0);break;
	}
	glutPostRedisplay();
}
void MenuCorbes(int tipus)
{
  switch (tipus) {
		case 1 :tipusCorba=POLIGONAL;break;
		case 2 :tipusCorba=BEZIER;break;
		case 3 :tipusCorba=BSPLINE;break;
	}
	glutPostRedisplay();
}

void menuMostrar(int tipus)
{
	switch (tipus)
	{
		case 1:mostrarMin*=-1;break;
		case 2:mostrarMax*=-1;break;
		case 3:mostrarMit*=-1;break;
	}
	glutPostRedisplay();
}
void CrearMenus()
{
	int submenu_curvas, menuprincipal_curvas, submenu_mostrar;

	//corbes
	submenu_curvas=glutCreateMenu(MenuCorbes);
	glutAddMenuEntry("Poligonal",1);
	glutAddMenuEntry("Bezier",2);
	glutAddMenuEntry("B-Spline",3);

	//mostrar
	submenu_mostrar=glutCreateMenu(menuMostrar);
	glutAddMenuEntry("Minim",1);
	glutAddMenuEntry("Maxim",2);
	glutAddMenuEntry("Mitja",3);

	//Principal
	menuprincipal_curvas=glutCreateMenu(menuPrincipal);
	glutAddSubMenu("Tipus de Corba",submenu_curvas);
	glutAddSubMenu("Mostrar/Ocultar",submenu_mostrar);
	glutAddMenuEntry("Sortir",4);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
}

void inicialitzacions(void)
{
	/*/Indiquem amb quin color es netejar el buffer*/
	glClearColor(1.0, 1.0, 1.0, 1.0);

	/*/Definim la matriu de projecci� 2D*/
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, ample, 0.0, alt);

	/*/Indiquem el tamany del Viewport*/
	glViewport(0,0,ample,alt);

	/*/I despres deixem activada la matriu del model,
	//per a introduir-hi mes dades*/
	glMatrixMode(GL_MODELVIEW);
	llegirDades();
}


int main(int argc, char** argv)
{
	/*Inicialitzacio finestra treball -> GLUT*/
	glutInit(&argc,argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);		/*<- FIxeu-vos que ha canviat!*/

	/*Creem la finestra de treball i la col.loquem*/
	glutInitWindowSize(ample,alt);
	glutInitWindowPosition(0,0);
	glutCreateWindow("Simulacio - Prac1");

	/*Programem la funcio que pintara*/
	glutDisplayFunc(Pintar);

	/*Inicialitzacio finestra renderitzacio -> OPENGL*/
	inicialitzacions();

    CrearMenus();
	/*Engeguem el bucle principal pq es maneguin els events*/
	glutMainLoop();
	return(0);
}

